import CryptoJS from 'crypto-js'
import defaultSettings from '@/settings'
import { isObject } from '@/utils/is'

import type { RequestEncryptionData, RequestConfig, RequestBaseConfig } from './types'

const { AESSecretKey, SHA1SecretKey, useMock, mockBaseURL } = defaultSettings

/**
 * 加密数据
 *
 * @param data 需要加密的数据
 * @returns 加密后的数据
 */
const encryption = (data: any): RequestEncryptionData => {
	// console.log('加密数据', data)
	const dataJSON = JSON.stringify(data)
	const encryptedData = CryptoJS.AES.encrypt(dataJSON, CryptoJS.enc.Base64.parse(AESSecretKey), {
		mode: CryptoJS.mode.ECB,
		padding: CryptoJS.pad.Pkcs7,
		length: 128,
	}).toString()
	const timestamp = new Date().getTime()
	// 签名
	const sign = CryptoJS.SHA1(encryptedData + timestamp + SHA1SecretKey).toString()
	return {
		sign: sign.toUpperCase(),
		data: encryptedData,
		timestamp,
	}
}

/**
 * 解密
 *
 * @param code 加密时的code参数
 * @param msg 加密时的msg参数
 * @param sign 加密时的签名
 * @param encryptedData 加密后的数据
 * @param timestamp 加密时的时间戳
 * @returns 解密后的JSON对象，如果签名验证失败则返回null
 */
const decryption = (code = '', msg = '', sign = '', encryptedData = '', timestamp = '') => {
	// 转码
	const decodeEncryptedData = decodeURI(encryptedData)
	// 验证签名
	const locationSign = CryptoJS.SHA1(code + msg + decodeEncryptedData + timestamp + SHA1SecretKey).toString()
	if (sign.toLowerCase() == locationSign) {
		const original = CryptoJS.AES.decrypt(decodeURIComponent(decodeEncryptedData), CryptoJS.enc.Base64.parse(AESSecretKey), {
			mode: CryptoJS.mode.ECB,
			padding: CryptoJS.pad.Pkcs7,
			length: 128,
		}).toString(CryptoJS.enc.Utf8)
		return JSON.parse(original)
	}
	return null
}

/**
 * 解析获取数据
 *
 * @param params 参数对象
 * @param isEncrypted 是否加密
 * @returns 返回包含签名、时间戳、频道和数据（加密或未加密）的对象
 */
const parseGetData = (params: any, isEncrypted: boolean) => {
	const { sign, data, timestamp } = encryption(params)
	return { sign, timestamp, data: isEncrypted ? data : params }
}

/**
 * 解析 POST 数据
 *
 * @param params 原始参数对象
 * @param isEncrypted 是否加密数据
 * @returns 解析后的 POST 数据对象，包含签名、时间戳、渠道和数据
 */
const parsePostData = (params: any, isEncrypted: boolean) => {
	const { sign, data, timestamp } = encryption(params)
	return {
		sign,
		timestamp,
		data: isEncrypted ? data : encodeURIComponent(JSON.stringify(params)),
	}
}

/**
 * 解析解密后的响应数据
 *
 * @param responseData 响应数据对象
 * @returns 返回解析后的响应数据对象
 */
const decryptionResolve = (responseData) => {
	const { code = 205, msg = '响应数据为空/错误', data = null, sign, timestamp = 0, decrypt = 0 } = responseData || {}
	if (!!code && !!msg && !!data && !!sign && !!timestamp) {
		const obj = decrypt === 1 ? decryption(code, msg, sign, data, timestamp) : JSON.parse(data)
		const result = {
			code,
			msg,
			data: obj,
		}
		return result
	} else {
		// 响应参数缺失 / 兼容mock数据
		const result = {
			code,
			msg,
			data: responseData.data,
		}
		return result
	}
}

/**
 * 加密参数
 *
 * @param method 请求方法
 * @param params 请求参数
 * @param isEncrypted 是否需要加密
 * @returns 返回加密后的参数对象
 */
const encryptionParams = <T = any>(baseConfig: Required<RequestBaseConfig<T>>) => {
	const { method, encryptable, params: _params, data: _data } = baseConfig
	const originEncrypted = method === 'get' ? { key: 'params', value: _params } : { key: 'data', value: _data }
	const encryptionData = encryption(originEncrypted.value)
	return { ...baseConfig, [originEncrypted.key]: encryptable ? encryptionData : originEncrypted.value }
}

/**
 * 生成请求基础配置
 *
 * @param config 请求配置
 * @param T 请求响应类型
 * @param D 请求数据类型
 * @returns 返回请求基础配置
 */

const genBaseConfig = <T = any, D = any>(config: RequestConfig<T, D>) => {
	const { baseURL = '', url = '', method = 'get', params, data } = config
	const _method = method?.toLowerCase()
	const _data = _method === 'get' ? params : data
	const {
		loading = false,
		encryptable = false,
		cancel = false,
		sortable = false,
		isReturnNativeResponse = false,
		showCatchErrorMessage = true,
		...resDate
	} = isObject(_data) ? _data : {}
	const extraRequestConfig = {
		encryptable: encryptable || config.encryptable,
		loading: loading || config.loading,
		cancel: cancel || config.cancel,
		sortable: sortable || config.sortable,
		isReturnNativeResponse: isReturnNativeResponse || config.isReturnNativeResponse,
		showCatchErrorMessage: showCatchErrorMessage || config.showCatchErrorMessage,
	}
	const _baseURL = useMock ? mockBaseURL : baseURL
	let _url = url ? (url.startsWith('/') ? url : '/' + url) : url
	_url = _url.startsWith(mockBaseURL) ? _url.replace(mockBaseURL, '') : _url
	return {
		baseURL: _baseURL,
		url: _url,
		method: _method,
		params: _method === 'get' ? resDate : undefined,
		data: _method === 'post' ? (resDate as any) : undefined,
		...extraRequestConfig,
	}
}

export { encryption, decryption, parseGetData, parsePostData, encryptionParams, decryptionResolve, genBaseConfig }
